package main

import "fmt"
import "flag"
import "demoutil"
import "os"
import "strconv"
import "bufio"
import "io"
//import "strings"

//import "math/rand"

// Distributed System
import "shardmaster"

var f_smh = flag.String("masters", "", "port number")
var f_auto = flag.String("file", "", "port number")
var mck *shardmaster.Clerk
var smh []string
var cmd demoutil.CMD

func main() {
	Init()
	if *f_auto == "" {
		//consolve version
		run := true
		line := ""
		in := bufio.NewReader(os.Stdin)
		var err error
		for run {
			fmt.Printf(">>>")
			// read space-seperated cmd into several arguments
			//fmt.Scanln(&line)
			line, err = in.ReadString('\n')
			if err != nil {
				if err == io.EOF {
					if line != "" {
						Execute(line)
					} 
					run = false
				} else {
					fmt.Printf("error reading from stdout: %v\n", err)
					os.Exit(1)
				}
			} else {
				Execute(line)
				run = cmd.GetStatus()
			}
		}
	} else {
		f, err := os.Open(*f_auto)
		if err != nil {
			fmt.Printf("error opening file: %v\n", err)
			os.Exit(1)
		}
		in := bufio.NewReader(f)
		s, e := in.ReadString('\n')
		for e == nil {
			fmt.Printf(">>> %v",s)
			Execute(s)
			s, e = in.ReadString('\n')
		}
	}
}
func Init() {
	//fmt.Println("Welcome, shardmaster Client :) ")
	// command parser
	cmd = demoutil.Make_cmd()
	// set up client
	flag.Parse()
	smh := demoutil.ParseAddresses(*f_smh)
	//fmt.Println("Connected to masters: ", smh)
	mck = shardmaster.MakeClerk(smh)
}

func Execute(line string) {
	execute := cmd.ParseInput(line)
	if execute {
		cc := cmd.GetTodo()
        err_msg := ""
		switch cc[0] {
		case "conn":
			fmt.Println("Server Connection:")
			for i := 0; i < len(smh); i++ {
				fmt.Printf("ShardMaster Server %v:  %v\n", i, smh[i])
			}
		case "join":
			if len(cc) != 3 {
				err_msg = "join: need two arguments: group id, shardkv group servers"
			} else {
				gid, err := strconv.Atoi(cc[1])
				if err != nil {
					err_msg = "first arguments group id should be an integer"
				} else {
					replicas := demoutil.ParseAddresses(cc[2])
					mck.Join(int64(gid), replicas)
					fmt.Println("OK")
				}
			}
		case "leave":
			if len(cc) != 2 {
				err_msg = "leave: need one arguments: group id"
			} else {
				gid, err := strconv.Atoi(cc[1])
				if err != nil {
					err_msg = "first arguments group id should be an integer"
				} else {
					mck.Leave(int64(gid))
					fmt.Println("OK")
				}
			}
		case "cfg", "shard", "group":
			cfg := mck.Query(-1)
			fmt.Printf("Configuration Num: %v\n", cfg.Num)
			fmt.Println("------------")
			if cc[0][0] == 'c' || cc[0][0] == 'g' {
				for k, v := range cfg.Groups {
					fmt.Printf("Gid %v: %v\n", k, v)
				}
			}
			if cc[0][0] == 'c' || cc[0][0] == 's' {
				fmt.Println("Shard: ", cfg.Shards)
			}
		case "query":
			cfg := mck.Query(-1)
			fmt.Println("Groups:")
			for k, v := range cfg.Groups {
				fmt.Printf("%v %v\n", k, v)
			}
			fmt.Printf("Shards:\n%v\n", cfg.Shards)
		default:
			err_msg = "command not found"

		}
		if err_msg != "" {
			fmt.Println("Error:" + err_msg)
		}
	}
}
